
// 1. 接收命令
// 2. 
#include "bs_command.h"
#include "bs_crc.h"
#include <string.h>

#define COMM_BUFFER_SIZE                 4096


#if FS_TYPE
FIL firm_file;
#define f2_write(p, b, s)                 f_write(p, b, s, NULL)
#define f2_close                          bs_imgr_save_image
#else
bs_file_t firm_file;
#endif

uint8_t flash_fw_buf[FILE_BLOCK_SIZE];
uint8_t command_buffer[COMM_BUFFER_SIZE];
extern const uint8_t bs_main_data[];
extern struct S_Writer m_app;
extern uint32_t recvTotal;

static recv_sta_t       recv_sta = RECV_STA_IDLE;
static uint32_t         hdr_stage = 0;

extern int cdc_transmit_package(uint8_t *data, uint32_t size);
extern int cdc_receive_continue(void);

/*!
    \brief 初始化一些静态数据
    \param[in]  none
    \param[out] none
    \retval
*/
void bs_comm_init()
{
    circ_buf_init(&m_app.m_circ_buffer, command_buffer, COMM_BUFFER_SIZE);
    circ_buf_reset(&m_app.m_circ_buffer);
    bs_comm_reset();
}


/*!
    \brief 中断上下文执行
    \param[in]  none
    \param[out] none
    \retval
*/
void bs_comm_reset()
{
    recv_sta = RECV_STA_IDLE;
    m_app.state = COMM_IDLE;
    m_app.ack_nbr = 0;
    hdr_stage = 0;

    // 清空数据缓存区
    m_app.m_circ_buffer.tail = 0;
    m_app.m_circ_buffer.head = 0;

    cdc_receive_continue();
}


void bs_comm_process()
{
    const static char       hdr_sync[] = "BSSTA";
    static S_Cmd_package_t  Cmd_pkg;
    uint8_t                 header;
    uint16_t                crc16_value;
    uint8_t                 crc_block[6];
    static U_pkg_func_t     *pkg_raw = (U_pkg_func_t *)m_app.data_packet;
    S_Cmd_SendDataPacket_t  *sd_pkg;

    // 接收日志
    static uint32_t         ext_sf_addr;
    static uint32_t         ext_sf_len;
    uint8_t                 *pData;
    bs_image_node_t         this_image;

    // 搜索 header "BSSTA";
    switch(recv_sta) {
        case RECV_STA_IDLE:
            while(hdr_stage < sizeof(hdr_sync) && circ_buf_count_used(&m_app.m_circ_buffer) != 0)
            {
                circ_buf_read(&m_app.m_circ_buffer, &header, 1);
                if (header == hdr_sync[hdr_stage]) {
                    hdr_stage++;
                } else {
                    if (hdr_stage == 0) continue;
                    hdr_stage = (header == hdr_sync[0]) ? 1: 0;
                }
            }
            if (hdr_stage >= sizeof(hdr_sync)) {
                recv_sta = RECV_STA_WAIT_FRAME;
                hdr_stage = 0;
            }
        break;

        case RECV_STA_WAIT_FRAME:
            if(circ_buf_count_used(&m_app.m_circ_buffer) > sizeof(S_Cmd_package_t)) {
                /* likely() */
                circ_buf_read(&m_app.m_circ_buffer, (uint8_t *)&Cmd_pkg, sizeof(S_Cmd_package_t));
                recv_sta = RECV_STA_WAIT_DATA;
            } else {
                // TODO: continue receive remain data.
            }
        break;

        case RECV_STA_WAIT_DATA:
            /* 找到数据帧了 */
            if (circ_buf_count_used(&m_app.m_circ_buffer) < Cmd_pkg.data_len) {
                cdc_receive_continue();
                break;
            }
            if(Cmd_pkg.data_len != 0)
            {
                circ_buf_read(&m_app.m_circ_buffer, m_app.data_packet, Cmd_pkg.data_len);
            }
            circ_buf_read(&m_app.m_circ_buffer, (uint8_t *)&m_app.crc16, 2);
            circ_buf_read(&m_app.m_circ_buffer, m_app.tail, sizeof(m_app.tail));
            *(uint16_t *)&crc_block[0] = Cmd_pkg.random;
            *(uint16_t *)&crc_block[2] = Cmd_pkg.cmd;
            *(uint16_t *)&crc_block[4] = Cmd_pkg.data_len;
            crc16_value = crc16(crc_block, 6, 0);
            if(Cmd_pkg.data_len != 0)
            {
                crc16_value = crc16(m_app.data_packet, Cmd_pkg.data_len, crc16_value);
            }
            if(crc16_value != m_app.crc16) {
                m_app.last_error = ERROR_FRAME_CRC16;
                bs_comm_response(CMD_ERROR, &m_app.last_error, 2);
                recv_sta = RECV_STA_IDLE;
                break;
            }
            recv_sta = RECV_STA_IDLE;
            m_app.state = COMM_DISPATCH_CMD;
            m_app.m_Cmd = (E_Command_type)Cmd_pkg.cmd;
        break;
        
        default:
        break;
    }
    // 解析命令, 下一次进入该函数会卡在
    switch (m_app.state) {
        case COMM_IDLE:
            // 等待接收新命令  
        case COMM_WAIT_CMD:
            __NOP();
        break;

        case COMM_DISPATCH_CMD:
            switch(m_app.m_Cmd) {
                case CMD_FIND_DEVICE:
                    bs_rtc_setup((uint8_t *)pkg_raw);
                    bs_comm_response(CMD_OK, NULL, 0);
                    m_app.state = COMM_IDLE;
                break;
                case CMD_ADD_IMAGE_START:
                    // 保存数据到 SRAM buffer
                    m_app.fw_len = pkg_raw->m_image_start.encrypted_file_len;

                    // 写入镜像文件
#if FS_TYPE
                    pData = (uint8_t *)pkg_raw->m_image_start.file_name;
                    this_image.download_key = 0;
                    this_image.encrypted_file_len = pkg_raw->m_image_start.encrypted_file_len;
                    this_image.encrypted_file_crc32 = pkg_raw->m_image_start.encrypted_file_crc32;
                    this_image.limit = 0xFFFF;
                    this_image.inv_limit = ~this_image.limit;
                    this_image.used_count = 0;
                    this_image.inv_used_count = ~this_image.used_count;
                    this_image.ok_count = 0;
                    this_image.inv_ok_count = ~this_image.ok_count;
                    memcpy(this_image.file_name, pData, strlen((char *)pData)+1);
                    bs_imgr_add_image(&this_image, &firm_file);
                    // f_open(&firm_file, (char *)pkg_raw->m_image_start.file_name, FA_CREATE_ALWAYS | FA_WRITE);
                    // f_open(&firm_file, (char *)pkg_raw->m_image_start.file_name, FA_CREATE_ALWAYS | FA_WRITE);
                    f_write(&firm_file, &m_app.fw_len, 4, NULL);
#else
                    firm_file.start_blk = 0;
                    firm_file.f_size = m_app.fw_len;
                    firm_file.f_buf_base = flash_fw_buf;
                    f2_open(&firm_file);
                    f2_write(&firm_file, &m_app.fw_len, 4);
#endif
                    bs_comm_response(CMD_OK, NULL, 0);
                    m_app.state = COMM_IDLE;
                break;

                case CMD_SEND_DATA_PACKET:
                    sd_pkg = &pkg_raw->m_send_packet;
                    f2_write(&firm_file, sd_pkg->data_array, sd_pkg->data_len);
                    bs_comm_response(CMD_OK, NULL, 0);
                    m_app.state = COMM_IDLE;

                break;

                case CMD_ADD_IMAGE_END:
                    // 文件结束
                    f2_close(&firm_file);
                    bs_comm_response(CMD_OK, NULL, 0);
                    m_app.state = COMM_IDLE;
                break;

                case CMD_CHIP_PROGRAM:
                    if (pkg_raw->m_chip_pg.len != m_app.fw_len) 
                    {
                        m_app.state = COMM_ERROR;
                        break;
                    }
                    // // 比较数据
                    // for(int i=0; i<m_app.fw_len; i++)
                    // {
                    //     if (flash_fw_buf[i] != bs_main_data[i])
                    //     {
                    //         __NOP();
                    //         m_app.state = COMM_ERROR;
                    //     }
                    //     m_app.state = COMM_IDLE;
                    // }
                    bs_comm_response(CMD_OK, NULL, 0);
                    m_app.state = COMM_IDLE;
                break;

                case CMD_GET_LOG:
                    // 初始化数据地址、长度
                    ext_sf_addr = pkg_raw->m_fetch_log.address;
                    ext_sf_len = pkg_raw->m_fetch_log.len;
                    bs_comm_response(CMD_OK, NULL, 0);
                    m_app.state = COMM_IDLE;
                break;
    
                case CMD_CLOSE_LOG:
                    bs_comm_response(CMD_OK, NULL, 0);
                    m_app.state = COMM_IDLE;
                break;
                
                case CMD_GET_DATA_PACKET:
                    // 这里要判断获取哪个设备的数据，可以借助 flash_fw_buf
                    ext_sf_len = *(uint16_t *)pkg_raw;
                    pData = flash_fw_buf;
                    *(uint16_t *)pData = ext_sf_len;
                    spi_flash_buffer_read_block(pData + 2, ext_sf_addr, ext_sf_len);
                    ext_sf_addr += ext_sf_len;
                    
                    // 把数据发出去
                    bs_comm_response(CMD_GET_DATA_PACKET_RESP, pData, ext_sf_len+2);
                    m_app.state = COMM_IDLE;
                break;
            
                default:
                break;
            }
        case COMM_CRC_ERROR:
            __NOP();
        break;
        case COMM_ERROR:
            // 应该复位

        break;

        default:
        break;
    }
}


/*!
    \brief      发送响应包
    \param[in]  none
    \param[out] none
    \retval     none
*/
static uint8_t resp_package[1024+128] = {'B', 'S', 'S', 'T', 'A', '\0'}; 
#define RANDOM_MAGIC  ((uint16_t)bs_jiffies)
int bs_comm_response(E_Command_type cmd, void *data_pkg, uint32_t size)
{
    static char tail[] = "BSEDN";
    S_Cmd_package_t *cmd_blk = (S_Cmd_package_t *)(resp_package+6);
    uint32_t offset = sizeof(S_Cmd_package_t) + 6;
    uint16_t crc_value;

    // 填充header部分
    cmd_blk->frame_num = m_app.ack_nbr++;
    cmd_blk->random = RANDOM_MAGIC;
    cmd_blk->cmd = (uint16_t)cmd;
    cmd_blk->data_len = size;

    // 数据部分
    if (size > 0 && data_pkg != NULL) {
        memcpy(resp_package+offset, data_pkg, size);
    }
    offset += size;

    // 计算 crc
    crc_value = crc16(resp_package + 13, offset - 13, 0);

    // 组装 crc 和 tail
    memcpy(resp_package + offset, &crc_value, 2);
    offset += 2;
    memcpy(resp_package + offset, tail, sizeof(tail));
    offset += sizeof(tail);

    // 发送数据
    cdc_transmit_package(resp_package, offset);

    return 0;
}
